/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algoritmogeneticoCHC;

import java.util.*; 
/* Esta clase corresponde a la población de todos los individuos del algoritmo genético.
 * 
 * Tiene como ATRIBUTOS:
 *  ** Un vector con entradas tipo IndividuoCHCs.
 *  ** El número de individuos de la población.
 *  ** La longitud de los genomas
 *  ** Número de bits para enteros
 *  ** Número de bits para decimales
 *  ** Número de variables por genoma
 *  ** La probabilidad de CrossOver
 *  ** La probabilidad de mutación
 *  ** Un generador de números aleatorios
 * 
 * Tiene como MÉTODOS:
 *  ** Genera población aleatoria
 *  ** Varios métodos para imprimir la población o parte de ella
 *  ** Fitness de la población (con normalización de linear offset)
 *  ** CDF del fitness
 *  ** Crossover
 *  ** Mutación puntual
 *  ** Selección proporcional
 *  ** Un método Quicksort para ordenar los individuos
 */
public class PoblacionCHC {
    
    public IndividuoCHC[] individuos;
    public int totalInd;
    public int longitudGen;
    public int bitsEnt;
    public int bitsDec;
    public int numVar;
    public double probXOver;
    public double denominadorThreshold;
    public Random alea;
    public boolean minimizar;
    
    //Inicializa un objeto población pero sin los individuos
    public PoblacionCHC(int num, int bE, int bD, int nV,double pxo, double pM, Random a, boolean min){
        totalInd = num;
        probXOver = pxo;
        individuos = new IndividuoCHC[num];
        denominadorThreshold = pM;
        bitsEnt = bE;
        bitsDec = bD;
        numVar = nV;
        longitudGen = (bitsEnt + bitsDec + 1)*numVar;
        alea = a;
        minimizar = min;
    }
    
    //Inicializa un objeto población copiando a la población que se da como parámetro
    public PoblacionCHC(PoblacionCHC pob2){
        totalInd = pob2.totalInd;
        probXOver = pob2.probXOver;
        individuos = new IndividuoCHC[totalInd];
        for(int i = 0; i<totalInd; i++){
             individuos[i] = new IndividuoCHC(pob2.individuos[i]);
        }
        denominadorThreshold = pob2.denominadorThreshold;
        bitsEnt = pob2.bitsEnt;
        bitsDec = pob2.bitsDec;
        numVar = pob2.numVar;
        longitudGen = (bitsEnt + bitsDec + 1)*numVar;
        alea = pob2.alea;
        minimizar = pob2.minimizar;
    }
    
     //Inicializa un objeto población copiando a la población A por completo
     // y solo los elementos que no están marcados con "eliminar = true" de la población B.
     // La n representa el verdadero número de individuos de la población B, es decir, los no eliminados.
    public PoblacionCHC(PoblacionCHC pobA, PoblacionCHC pobB, int n){
        //El tamaño de la población es el de A más los que no hay que eliminar de B
        totalInd = pobA.totalInd + n;
        probXOver = pobA.probXOver;
        individuos = new IndividuoCHC[totalInd];
        //Copia población A
        for(int i = 0; i<pobA.totalInd; i++){ 
            individuos[i]= new IndividuoCHC(pobA.individuos[i]);
        }
        //Copia población B
        int i=0;
        int j = 0;
        while(i<n){
            if(!pobB.individuos[j].eliminar){
                individuos[i + pobA.totalInd] = new IndividuoCHC(pobB.individuos[j]);
                i++;
            }
            j++;
        }
        denominadorThreshold = pobA.denominadorThreshold;
        bitsEnt = pobA.bitsEnt;
        bitsDec = pobA.bitsDec;
        numVar = pobA.numVar;
        longitudGen = (bitsEnt + bitsDec + 1)*numVar;
        alea = pobA.alea;
        minimizar = pobA.minimizar;
    }
    
    public void SeleccionaPrimerosN(PoblacionCHC pobA,int numInds){
        totalInd = numInds;
        probXOver = pobA.probXOver;
        individuos = new IndividuoCHC[totalInd];
        //Copia población A
        for(int i = 0; i<totalInd; i++){ 
            individuos[i]= new IndividuoCHC(pobA.individuos[i]);
        }
        denominadorThreshold = pobA.denominadorThreshold;
        bitsEnt = pobA.bitsEnt;
        bitsDec = pobA.bitsDec;
        numVar = pobA.numVar;
        longitudGen = (bitsEnt + bitsDec + 1)*numVar;
        alea = pobA.alea;
        minimizar = pobA.minimizar;
    }
            
    //Genera aleatoriamente a los individuos de la población
    public void CreaPoblacionCHCInicial(){
        individuos = new IndividuoCHC[totalInd];
        for(int i = 0; i< totalInd; i++){
            individuos[i] = new IndividuoCHC(bitsEnt, bitsDec, longitudGen,numVar, denominadorThreshold,alea);
            individuos[i].CreaAleatorio();
        }
    }
    
    //Imprime a todos todos los individuos de la población y sus atributos
    public void ImprimePoblacionCHC(){
        for(int i = 0; i<totalInd; i++){
            System.out.println("");
            System.out.println("IndividuoCHC "+i+": ");
            individuos[i].ImprimeTodo();
        }
    }
    
    //Imprime a los individuos de la población, sus atributos y el valor de las 
    //variables que están codificadas en su genoma
    public void ImprimePoblacionCHCConValores(){
            for(int i = 0; i<totalInd; i++){
            System.out.println("");
            System.out.println("IndividuoCHC "+i+": ");
            individuos[i].ImprimeTodo();
            individuos[i].ImprimeValores();
        }
    }
    
    //Imprime los genomas de la población
    public void ImprimeGenomas(){
        for(int i = 0; i<totalInd; i++){
            System.out.println("");
            System.out.println("IndividuoCHC "+i+": ");
            individuos[i].ImprimeGenoma();
        }
    }
    
    //Imprime el fitness del mejor individuo de la población y los valores de las
    //variables codificadas en su genoma
    public void ImprimeOptimo(){
        System.out.println("\nResultado: ");
        individuos[0].ImprimeResultado();
    }
    
    public String RegresaOptimoString(){
        return individuos[0].RegresaOptimoString();
       
    }
    
    //Muta a todos los individuos de la población
    public void Muta(){
        for(int i = 0; i<totalInd; i++){
            individuos[i].Muta();
        }
    }
    
    public void MutacionCataclismica(){
        for(int i = 2; i <totalInd; i++){
            individuos[i].MutaCataclismo();
        }
    }
    
    /*Este método crea la población C del algorimto CHC a partir de los elementos de la 
     * población pobA. Regresa la nueva población recién creada. 
     */
    public void PobCHUX(){
        double threshold = longitudGen/denominadorThreshold;
        for(int i = 0; i< totalInd; i+=2){
           int escogeInd1 = (int)Math.floor(alea.nextDouble()*totalInd);
           int escogeInd2 = (int)Math.floor(alea.nextDouble()*totalInd);
           individuos[escogeInd1].HUX(individuos[escogeInd2],threshold);
       }
    }
    
    
    //Método que calcula el fitness 
    public void Fitness(int opcion){
            for(int i = 0; i < totalInd; i++){
                individuos[i].FitnessCHC(opcion);
            }    
    }
    
    //Método que cuenta cuántos individuos no tienen que eliminarse
    public int TamanoReal(){
        int n = 0;
        for(int i = 0; i<totalInd; i++){
            if(!individuos[i].eliminar){
                n++;
            }
        }
        return n;
    }
    
    //Método que revisa si dos poblaciones son iguales
    //Las poblaciones tienen que ser del mismo tamaño
    public boolean Iguales(PoblacionCHC pobA){
        boolean iguales = true;
        int i = 0;
        //Ciclo que se para cuando encuentra una diferencia entre las poblaciones
        // o hasta que revisa todos los elementos de la población.
        while(iguales && i < totalInd){ 
            //Si dos individuos tienen el mismo genoma, entonces son iguales
            if(!individuos[i].Iguales(pobA.individuos[i].genoma)){
                iguales = false;
            }
            else{
                i++;
            }
        }
        return iguales;
    }
    
    //Función que calcula el promedio de los fitnesses absolutizados
    public double PromDeAbsolutos(){
        double prom = 0;
        for(int i = 0; i<totalInd; i++){
            prom = prom + Math.abs(individuos[i].fitness);
        }
        prom = prom/totalInd;
        return prom;
    }
    
    /*Método que ordena al vector de individuos en orden descendente de acuerdo 
    a su fitness normalizado. Después de que están en orden, va sumando los 
    fitnesses para obtener la CDF. */
    public void OrdenaPorCDF(){
       QuickSortFitness(0,totalInd-1);
       double acumulaB, acumulado = 0; 
       for(int i = 0; i < totalInd; i++){
           acumulaB = individuos[i].AcumulaCDF(acumulado);
           acumulado = acumulaB;
       }
    }
    
     /*Método que ordena al vector de individuos en orden descendente (para minimizar = true) 
    o en orden ascendente (para minimzar = false) de acuerdo a su fitness.*/
    public void OrdenaPorFitness(){
       QuickSortFitness(0,totalInd-1);
    }
    
    //Método que ordena a los elementos mayores que el elemento del centro del lado izquierdo
    //y a los elementos menores que el elemento del centro del lado derecho.
    int Particion(int izq, int der){
      int i = izq, j = der;
      IndividuoCHC temp;
      IndividuoCHC pivote = individuos[(int)((izq + der)/2)];
      
        while (i <= j) {
            while (individuos[i].Compara(pivote) > 0){ //Estoy arreglando de mayor a menor CDF
                  i++;}
            while (individuos[j].Compara(pivote)<0){//De nuevo, los chicos al final.
                  j--;}
            if (i <= j) {
                  temp = new IndividuoCHC(individuos[i]);
                  individuos[i] = new IndividuoCHC(individuos[j]);
                  individuos[j] = new IndividuoCHC(temp);
                  i++;
                  j--;
            }
        }
      return i;
    }
    
    /*Método que ordena los elementos de un vector llamando a Partición. Cuando
     * minimizar = false es como lo tenía originalmente. Si minimizar=true es
     * lo que hay que probar.
     */
    public void QuickSortFitness(int izq, int der){
      int indice = ParticionFitness(izq, der);
           if (izq < indice - 1){
                QuickSortFitness(izq, indice-1);
            }
            if (indice < der){
                QuickSortFitness(indice, der);
            }
    }
    

    //Método que ordena a los elementos mayores que el elemento del centro del lado izquierdo
    //y a los elementos menores que el elemento del centro del lado derecho.
    int ParticionFitness(int izq, int der){
      int i = izq, j = der;
      IndividuoCHC temp;
      IndividuoCHC pivote = individuos[(int)((izq + der)/2)];
      if(!minimizar){
      while (i <= j) {
            while (individuos[i].ComparaFitness(pivote) > 0){ //Estoy arreglando de mayor a menor fitness
                  i++;}
            while (individuos[j].ComparaFitness(pivote)<0){//De nuevo, los chicos al final.
                  j--;}
            if (i <= j) {
                  temp = new IndividuoCHC(individuos[i]);
                  individuos[i] = new IndividuoCHC(individuos[j]);
                  individuos[j] = new IndividuoCHC(temp);
                  i++;
                  j--;
            }
      }
      }
      else{  
          while (i <= j) {
            while (individuos[i].ComparaFitness(pivote) < 0){ //Estoy arreglando de menor a mayor fitness
                  i++;}
            while (individuos[j].ComparaFitness(pivote) > 0){//De nuevo, los chicos al principio.
                  j--;}
            if (i <= j) {
                  temp = new IndividuoCHC(individuos[i]);
                  individuos[i] = new IndividuoCHC(individuos[j]);
                  individuos[j] = new IndividuoCHC(temp);
                  i++;
                  j--;
            }
        }
      }
      return i;
    }
    
    public SchemaTool ObtenEsquema() {
        SchemaTool esquema = new SchemaTool(longitudGen);
        for (int i = 0; i < individuos.length; i++) {
            esquema.sum(individuos[i].genoma);
        }
        return esquema;
    }
}
